#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <WebServer.h>
#include <SPIFFS.h>
#include <Preferences.h>
#include <ArduinoJson.h>

// 设置wifi连接名称与密码
String ssid = "";
String password = "";

// MQTT服务器地址和端口
const char* mqtt_server = "42.193.19.33";
const int mqtt_port = 1883;
const char* mqtt_username = "yunhe";
const char* mqtt_password = "yunhe@2020";

// MQTT客户端ID、主题消息
const char* client_id = "esp32-led-control-lushan";
const char* topic = "topic/led_control/lushan";
const char* playload = "你好，世界。";

// 定义连接LED的引脚
const int ledPin = 13;
const int wifiLedPin = 2;

// 定义闪烁间隔时间
const int blinkInterval = 500; // 500毫秒

// WIFI与MQTT客户端
WiFiClient wifiClient;
PubSubClient client(wifiClient);
// 定义web服务器端口
WebServer server(80);
// nvs存储
Preferences preferences;

void setupServer() {
    delay(1000);
    // 初始化 SPIFFS
    if (!SPIFFS.begin(true)) {
        Serial.println("SPIFFS 初始化失败");
        return;
    }
    // 启动esp32的AP模式
    bool apStarted = WiFi.softAP("LED-Control-AP", "12345678");

    if (apStarted) {
        Serial.println("AP 模式已启动");
        Serial.print("AP IP address: ");
        Serial.println(WiFi.softAPIP());
    } else {
        Serial.println("AP 模式启动失败");
        return;
    }

    // 提供静态文件服务
    server.serveStatic("/", SPIFFS, "/index.html");

    // 处理用户提交的表单数据
    server.on("/configure", []() {
        ssid = server.arg("ssid");
        password = server.arg("password");

        // 将收到的SSID和密码保存到NVS
        preferences.begin("wifi-config", false);
        preferences.putString("ssid", ssid);
        preferences.putString("password", password);
        preferences.end();

        // 将收到的 SSID 和密码打印到串口监视器
        Serial.println("接收到ssid：" + ssid);
        Serial.println("接收到密码：" + password);

        // 返回确认消息给用户
        File file = SPIFFS.open("/success.html", "r");
        if (file) {
            server.streamFile(file, "text/html");
            file.close();
        } else {
            server.send(200, "text/html", "Configuration received, rebooting ESP...");
        }

        // 重启设备
        delay(1000);
        ESP.restart();
    });

    server.begin();
    Serial.println("Web 服务器已启动");
}

// mqtt回调
void callback(char* topic, byte* payload, unsigned int length) {
    Serial.print("收到消息 [");
    Serial.print(topic);
    Serial.print("] ");
    Serial.println(length);

    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, payload);
    if (error) {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return;
    }

    String mode = doc["mode"];
    if(mode == "wifi") {
        preferences.begin("wifi-config", false);
        preferences.putString("ssid", doc["ssid"].as<String>());
        preferences.putString("password", doc["password"].as<String>());
        preferences.end();
        ESP.restart();
    }

    digitalWrite(ledPin, HIGH);
    delay(200);
    digitalWrite(ledPin, LOW);
}

/**
 * 监听wifi状态变化
 */
void wifiEvent(WiFiEvent_t event) {
    switch (event) {
    case SYSTEM_EVENT_STA_GOT_IP:
        Serial.println("WiFi已连接");
        digitalWrite(wifiLedPin, HIGH);
        break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
        Serial.println("WiFi已断开连接");
        digitalWrite(wifiLedPin, LOW);
        break;
    default:
        break;
    }
}

/**
 * wifi连接
 */
void setupWifi() {
  delay(10);

    // 设置WiFi模式为STA（Station模式）
    WiFi.mode(WIFI_STA);

    // 注册监听事件
    WiFi.onEvent(wifiEvent);

  // 开始连接WiFi
  WiFi.begin(ssid, password);
  Serial.print("正在连接WIFI");

  // 等待连接，最多等待10秒
  unsigned long startAttemptTime = millis();
  while(WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 10000) {
    delay(1000);
    Serial.print(" .");
  }

  // 检查是否连接成功
  if(WiFi.status() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("连接成功！");
    Serial.print("IP地址: ");
    Serial.println(WiFi.localIP());

    // 连接mqtt
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(callback);
  } else {
    // setupServer();
    // 清除wifi信息并重启
    preferences.begin("wifi-config", false);
    preferences.clear();
    ESP.restart();
  }
}

// mqtt 连接
void reconnect() {
  // 循环直到重新连接
  while(!client.connected()) {
    Serial.println("正在尝试 MQTT 连接……");
    // 尝试连接
    if(client.connect(client_id, mqtt_username, mqtt_password)) {
      Serial.println("连接成功！");
      client.subscribe(topic);
    } else {
      Serial.print("失败: ");
      Serial.print(client.state());
      Serial.println("5秒后重试。");
      // 等待5秒再次尝试
      delay(5000);
    }
  }
}

// setup() 函数将在程序启动或ESP32复位时运行一次
void setup() {
    Serial.begin(115200);

    // 设置ledPin为输出模式
    pinMode(ledPin, OUTPUT);
    pinMode(wifiLedPin, OUTPUT);

     // 将收到的 SSID 和密码打印到串口监视器
    // 从NVS读取WiFi配置
    preferences.begin("wifi-config", true);
    ssid = preferences.getString("ssid", "");
    password = preferences.getString("password", "");
    preferences.end();
    Serial.println("ssid：" + ssid);
    Serial.println("password：" + password);

    if(ssid.length() > 0) {
        // 连接wifi
        setupWifi();
        // 在串口监视器上打印一条信息
        // Serial.println("LED闪烁程序启动");
    } else {
        // 启动web服务器
        setupServer();
    }
}

// loop() 函数将不断循环运行
void loop() {
    // 处理 Web 服务器请求
    server.handleClient();

    if(!client.connected() && ssid.length() > 0) {
        reconnect();
    }
    client.loop();

    // LED闪烁逻辑
    if(WiFi.status() != WL_CONNECTED) {
        static unsigned long lastBlinkTime = 0;
        static bool ledState = LOW;
        unsigned long currentMillis = millis();
        if (currentMillis - lastBlinkTime >= blinkInterval) {
            // 切换LED状态
            ledState = !ledState;
            digitalWrite(wifiLedPin, ledState);
            lastBlinkTime = currentMillis;
        }
    }

    // // 点亮led
    // digitalWrite(ledPin, HIGH);
    // // 在串口监视器上打印led状态
    // Serial.println("LED点亮");
    // // 延时500毫秒
    // delay(500);

    // // 关闭led
    // digitalWrite(ledPin, LOW);
    // // 在串口监视器上打印led状态
    // Serial.println("LED关闭");
    // // 延时500毫秒
    // delay(500);

    // // 发送消息
    // client.publish(topic, playload);
}